Skip to main content

Log Format

The access log records every HTTP request handled by NGINX.

It is essential for:

  • Security monitoring (attacks, scans, abuse)
  • Performance analysis (latency, bottlenecks)
  • Debugging application issues
  • Traffic analytics
  • Compliance and auditing

Each log entry represents one completed request.

How Access Logging Works Internally

  1. Client sends request
  2. NGINX processes request
  3. Response is sent
  4. After response, NGINX writes a log line using:
    • A log format
    • A log destination (file/syslog/stdout)

Core Directives for Access Logs

log_format (Define Log Structure)

log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

Defines what data is logged and how it is structured

access_log (Enable Logging)

access_log /var/log/nginx/access.log main;

Uses:

  • Log file path
  • Log format name

Default NGINX Access Log Format (Combined)

NGINX’s default format is similar to Apache’s combined format:

log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

Example Log Entry

203.0.113.10 - - [22/Jan/2026:10:15:43 +0000]
"GET /login HTTP/1.1" 200 532
"https://example.com" "Mozilla/5.0"

Explanation of Common Access Log Variables

VariableDescriptionSecurity / Monitoring Use
$remote_addrClient IP addressIdentify attackers
$remote_userAuthenticated userAudit
$time_localRequest timeIncident timeline
$requestFull request lineDetect exploits
$statusHTTP response codeError monitoring
$body_bytes_sentResponse sizeTraffic analysis
$http_refererReferrer URLCSRF detection
$http_user_agentClient softwareBot detection
log_format security '$remote_addr '
'[$time_iso8601] '
'"$request" '
'$status '
'$request_time '
'"$http_user_agent"';

Example Entry

198.51.100.25 [2026-01-22T10:17:11+00:00]
"POST /login HTTP/1.1" 401 0.532 "curl/7.88.1"

Performance & Latency Monitoring Format

log_format performance '$remote_addr '
'"$request" '
'$status '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time';

Example

203.0.113.5 "GET /api HTTP/1.1" 200 rt=0.120 uct=0.003 uht=0.020 urt=0.115
FieldMeaning
rtTotal request time
uctUpstream connect time
uhtTime to receive headers
urtBackend response time

JSON Access Log Format (Best for Modern Monitoring)

log_format json escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"request_time":$request_time,'
'"user_agent":"$http_user_agent"'
'}';

Example JSON Log

{
"time":"2026-01-22T10:19:12+00:00",
"remote_addr":"192.0.2.15",
"method":"GET",
"uri":"/api/users",
"status":200,
"request_time":0.083,
"user_agent":"Mozilla/5.0"
}

Conditional Access Logging (Reduce Noise)

Log Only Errors (Status ≥ 400)

map $status $log_errors {
~^[45] 1;
default 0;
}

access_log /var/log/nginx/error_access.log security if=$log_errors;

Disable Access Logging (Static Content)

location /assets/ {
access_log off;
expires 30d;
}

Logging Real Client IP (Behind Proxy)

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Then log:

log_format realip '$remote_addr "$request" $status';

Common Logging Mistakes

MistakeImpact
Logging without request timeHard to debug slowness
Not using JSON logsPainful parsing
Missing real IPWrong attacker identity
Logging everythingDisk exhaustion
No log rotationServer crash

Log Rotation (Essential)

NGINX does not rotate logs itself.

Use logrotate:

/var/log/nginx/*.log {
daily
rotate 14
compress
missingok
notifempty
create 0640 nginx adm
postrotate
systemctl reload nginx
endscript
}
log_format secure_json escape=json
'{'
'"time":"$time_iso8601",'
'"ip":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"rt":$request_time,'
'"ua":"$http_user_agent"'
'}';

access_log /var/log/nginx/access.log secure_json;